/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Foundation Framework Class Library
 * Version 2.00
 * 
 * Virtual Memory Stream Class
 *      Provides a memory stream implementation which will page content to disk
 * when the size reaches the memory maximum.
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using System.IO;
using System.Threading;

namespace Adaptive.Foundation.IO
{
    /// <summary>
    /// Provides a memory stream implementation which will page content to disk
    /// when the size reaches the memory maximum.
    /// </summary>
    public class VirtualMemoryStream : Stream
    {
        /*--------------------------------------------------------------------------------
         * Public Event Definitions
         *------------------------------------------------------------------------------*/
        #region Public Event Definitions
        /// <summary>
        /// Occurs when the stream is closed.
        /// </summary>
        public event EventHandler StreamClosed;
        /// <summary>
        /// Occurs when the memory boundary is reached.
        /// </summary>
        public event EventHandler MaxMemoryReached;
        /// <summary>
        /// Occurs just before the memory data is paged to disk.
        /// </summary>
        public event EventHandler PageOut;
        /// <summary>
        /// Occurs just before the memory data is moved to disk to memory.
        /// </summary>
        public event EventHandler PageIn;
        /// <summary>
        /// Occurs when the paging operation is completed.
        /// </summary>
        public event EventHandler PageComplete;
        /// <summary>
        /// Occurs when a stream-oriented exception is encountered.
        /// </summary>
        public event ExceptionEventHandler StreamException;

        #endregion

        /*--------------------------------------------------------------------------------
         * Private Constants Declarations
         *------------------------------------------------------------------------------*/
        #region Private Constants Declarations
        /// <summary>
        /// Class name.
        /// </summary>
        private const string CLASS_NAME = "VirtualMemoryStream";
        /// <summary>
        /// Default maximum memory value.
        /// </summary>
        private const int DEF_MAX_MEMORY = 268435455;
        /// <summary>
        /// Data block size.
        /// </summary>
        private const int DEF_BLOCK_SIZE = 524288;
        /// <summary>
        /// Data block size.
        /// </summary>
        private const int COPY_BLOCK_SIZE = 524288;
        /// <summary>
        /// Number of bytes in 256 MB of memory.
        /// </summary>
        private const int MEMORYSIZE_256MB = 268435456;
        /// <summary>
        /// Number of bytes in 512 MB of memory.
        /// </summary>
        private const int MEMORYSIZE_512MB = 536870912;
        /// <summary>
        /// Number of bytes in 1 GB of memory.
        /// </summary>
        private const int MEMORYSIZE_1024MB = 1073741824;
        /// <summary>
        /// Number of bytes in 2 GB of memory.
        /// </summary>
        private const int MEMORYSIZE_2048MB = 2147483647;
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Member Declarations
         *------------------------------------------------------------------------------*/
        #region Private Member Declarations
        /// <summary>
        /// Stream pointer.
        /// </summary>
        private Stream _streamPointer;
        /// <summary>
        /// Memory stream.
        /// </summary>
        private MemoryStream _memoryStream;
        /// <summary>
        /// Temporary file stream.
        /// </summary>
        private FileStream _fileStream;
        /// <summary>
        /// Data reader.
        /// </summary>
        private BinaryReader _reader;
        /// <summary>
        /// Data writer.
        /// </summary>
        private BinaryWriter _writer;
        /// <summary>
        /// Thread double-lock mechanism.
        /// </summary>
        private ReaderWriterLock _lock;
        /// <summary>
        /// Name of temporary file, when in use.
        /// </summary>
        private string _tempFileName = string.Empty;
        /// <summary>
        /// Last known size of the underlying stream.
        /// </summary>
        private long _lastSize;
        /// <summary>
        /// Maximum memory boundary size.
        /// </summary>
        private long _memoryBoundary  = DEF_MAX_MEMORY;
        #endregion

        /*--------------------------------------------------------------------------------
         * Constructor / Dispose / Destructor Methods
         *------------------------------------------------------------------------------*/
        #region Constructor / Dispose / Destructor Methods
        /// <summary>
        /// Initializes a new instance of the class.
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        public VirtualMemoryStream()
            : base()
        {
            //Determine the maximum memory boundary size.
            _memoryBoundary = DEF_MAX_MEMORY;

            //Create the default stream.
            InitializeMemoryStream();
        }
        /// <summary>
        /// Initializes a new instance of the class.
        /// </summary>
        /// <param name="detectMaxMemory">
        /// A value indicating whether to detect the maximum memory size or to use the default value.
        /// </param>
        public VirtualMemoryStream(bool detectMaxMemory)
            : base()
        {
            //Determine the maximum memory boundary size.
            if (detectMaxMemory)
                _memoryBoundary = FindMaximumMemoryBoundary();

            //Create the default stream.
            InitializeMemoryStream();
        }
        /// <summary>
        /// Initializes a new instance of the class to the default capacity.
        /// </summary>
        /// <param name="capacity">
        /// The initial size of the stream, in bytes.
        /// </param>
        public VirtualMemoryStream(int capacity)
            : base()
        {
            //Determine the maximum memory boundary size.
            _memoryBoundary = DEF_MAX_MEMORY;

            //Create the default stream.
            if (capacity <= _memoryBoundary)
                InitializeMemoryStream(capacity);
            else
                InitializeFileStream(capacity);

        }
        /// <summary>
        /// Initializes a new instance of the class to the default capacity.
        /// </summary>
        /// <param name="capacity">
        /// The initial size of the stream, in bytes.
        /// </param>
        /// <param name="detectMaxMemory">
        /// A value indicating whether to detect the maximum memory size or to use the default value.
        /// </param>
        public VirtualMemoryStream(int capacity, bool detectMaxMemory)
            : base()
        {
            //Determine the maximum memory boundary size.
            if (detectMaxMemory)
                _memoryBoundary = FindMaximumMemoryBoundary();

            //Create the default stream.
            if (capacity <= _memoryBoundary)
                InitializeMemoryStream(capacity);
            else
                InitializeFileStream(capacity);

        }
        /// <summary>
        /// Initializes a new instance of the class to the default capacity.
        /// </summary>
        /// <param name="capacity">
        /// The initial size of the stream, in bytes.
        /// </param>
        public VirtualMemoryStream(long capacity)
            : base()
        {
            //Determine the maximum memory boundary size.
            _memoryBoundary = DEF_MAX_MEMORY;

            //Create the default stream.
            if (capacity <= _memoryBoundary)
                InitializeMemoryStream(capacity);
            else
                InitializeFileStream(capacity);

        }
        /// <summary>
        /// Initializes a new instance of the class to the default capacity.
        /// </summary>
        /// <param name="capacity">
        /// The initial size of the stream, in bytes.
        /// </param>
        /// <param name="detectMaxMemory">
        /// A value indicating whether to detect the maximum memory size or to use the default value.
        /// </param>
        public VirtualMemoryStream(long capacity, bool detectMaxMemory)
            : base()
        {
            //Determine the maximum memory boundary size.
            if (detectMaxMemory)
                _memoryBoundary = FindMaximumMemoryBoundary();

            //Create the default stream.
            if (capacity <= _memoryBoundary)
                InitializeMemoryStream(capacity);
            else
                InitializeFileStream(capacity);
        }
        /// <summary>
        /// Initializes a new instance of the class for the specified data
        /// content.
        /// </summary>
        /// <param name="content">
        /// The initial data to be contained in the stream instance.
        /// </param>
        public VirtualMemoryStream(byte[] content)
            : base()
        {
            //Determine the maximum memory boundary size.
            _memoryBoundary = DEF_MAX_MEMORY;

            //Create the default stream.
            if (content.Length <= _memoryBoundary)
                InitializeMemoryStream(content);
            else
                InitializeFileStream(content);
        }
        /// <summary>
        /// Initializes a new instance of the class for the specified data
        /// content.
        /// </summary>
        /// <param name="content">
        /// The initial data to be contained in the stream instance.
        /// </param>
        /// <param name="detectMaxMemory">
        /// A value indicating whether to detect the maximum memory size or to use the default value.
        /// </param>
        public VirtualMemoryStream(byte[] content, bool detectMaxMemory)
            : base()
        {
            //Determine the maximum memory boundary size.
            if (detectMaxMemory)
                _memoryBoundary = FindMaximumMemoryBoundary();

            //Create the default stream.
            if (content.Length <= _memoryBoundary)
                InitializeMemoryStream(content);
            else
                InitializeFileStream(content);
        }
        /// <summary>
        /// Deallocates internally allocated objects.
        /// </summary>
        /// <param name="disposing">
        /// A value indicating whether the object is being disposed.
        /// </param>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                Close();
            }

            _reader = null;
            _writer = null;
            _streamPointer = null;
            _fileStream = null;
            _memoryStream = null;
            _tempFileName = null;
            base.Dispose(disposing);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Properties
         *------------------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets a reference to the underlying stream.
        /// </summary>
        /// <value>
        /// A <see cref="FileStream"/>, <see cref="MemoryStream"/>, or <b>null</b>.
        /// </value>
        public Stream BaseStream
        {
            get
            {
                return _streamPointer;
            }
        }
        /// <summary>
        /// Gets a value indicating whether the current stream supports reading.
        /// </summary>
        /// <value>
        /// <b>true</b> if reading is supported; otherwise, <b>false</b>.
        /// </value>
        public override bool CanRead
        {
            get { return ((_streamPointer != null) && (_streamPointer.CanRead)); }
        }
        /// <summary>
        /// Gets a value indicating whether the current stream supports writing.
        /// </summary>
        /// <value>
        /// <b>true</b> if writing is supported; otherwise, <b>false</b>.
        /// </value>
        public override bool CanWrite
        {
            get { return ((_streamPointer != null) && (_streamPointer.CanWrite)); }
        }
        /// <summary>
        /// Gets a value indicating whether the current stream supports seeking.
        /// </summary>
        /// <value>
        /// <b>true</b> if seeking is supported; otherwise, <b>false</b>.
        /// </value>
        public override bool CanSeek
        {
            get { return ((_streamPointer != null) && (_streamPointer.CanSeek)); }
        }
        /// <summary>
        /// Gets a value that determines whether the current stream can time out.
        /// </summary>
        /// <value>
        /// Always <b>false</b>.
        /// </value>
        public override bool CanTimeout
        {
            get
            {
                return false;
            }
        }
        /// <summary>
        /// Gets the current size of the stream, in bytes.
        /// </summary>
        /// <value>
        /// The number of bytes in the current stream.
        /// </value>
        public override long Length
        {
            get
            {
                if (_streamPointer == null) 
                    return 0;
                else
                    return _streamPointer.Length;
            }
        }
        /// <summary>
        /// Gets or sets the position within the current stream.
        /// </summary>
        /// <value>
        /// The current position, as a byte index / position value.
        /// </value>
        public override long Position
        {
            get
            {
                if (_streamPointer == null) 
                    return 0;
                else
                    return _streamPointer.Position;
            }
            set
            {
                if (_streamPointer != null)
                    _streamPointer.Position = value;
            }
        }
        /// <summary>
        /// Gets or sets a value that determines how long the stream will attempt to 
        /// read before it times out.
        /// </summary>
        /// <value>
        /// Always zero (0).
        /// </value>
        public override int ReadTimeout
        {
            get
            {
                return 0;
            }
            set
            {
            }
        }
        /// <summary>
        /// Gets or sets a value that determines how long the stream will attempt to 
        /// write before it times out.
        /// </summary>
        /// <value>
        /// Always zero (0).
        /// </value>
        public override int WriteTimeout
        {
            get
            {
                return 0;
            }
            set
            {
            }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Protected Methods / Event Methods
         *------------------------------------------------------------------------------*/
        #region Protected Methods / Event Methods
        /// <summary>
        /// Raises the <see cref="StreamClosed"/> event.
        /// </summary>
        /// <value>
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </value>
        protected virtual void OnStreamClosed(EventArgs e)
        {
            if (StreamClosed != null)
                StreamClosed(this, e);
        }
        /// <summary>
        /// Raises the <see cref="MaxMemoryReached"/> event.
        /// </summary>
        /// <value>
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </value>
        protected virtual void OnMaxMemoryReached(EventArgs e)
        {
            if (MaxMemoryReached != null)
                MaxMemoryReached(this, e);
        }
        /// <summary>
        /// Raises the <see cref="PageOut"/> event.
        /// </summary>
        /// <value>
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </value>
        protected virtual void OnPageOut(EventArgs e)
        {
            if (PageOut != null)
                PageOut(this, e);
        }
        /// <summary>
        /// Raises the <see cref="PageIn"/> event.
        /// </summary>
        /// <value>
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </value>
        protected virtual void OnPageIn(EventArgs e)
        {
            if (PageIn != null)
                PageIn(this, e);
        }
        /// <summary>
        /// Raises the <see cref="PageComplete"/> event.
        /// </summary>
        /// <value>
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </value>
        protected virtual void OnPageComplete(EventArgs e)
        {
            if (PageComplete != null)
                PageComplete(this, e);
        }
        /// <summary>
        /// Raises the <see cref="StreamException"/> event.
        /// </summary>
        /// <value>
        /// An <see cref="ExceptionEventArgs"/> event arguments instance 
        /// containing the exception that was caught.
        /// </value>
        protected virtual void OnStreamException(ExceptionEventArgs e)
        {
            if (StreamException != null)
                StreamException(this, e);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Public Methods / Functions
        /// <summary>
        /// Closes the current stream.
        /// </summary>
        public override void Close()
        {
            if (_streamPointer != null)
            {
                //Close the reader and writer.
                if (_reader != null)
                    _reader.Close();
                if (_writer != null)
                    _writer.Close();

                //Close the stream instance.
                if (_memoryStream != null)
                {
                    _memoryStream.Close();
                    _memoryStream.Dispose();
                }
                if (_fileStream != null)
                {
                    _fileStream.Close();
                    _fileStream.Dispose();
                    //Delete the temporary file, if present.
                    SafeDeleteTempFile();
                }

                //Clear memory.
                _reader = null;
                _writer = null;
                _fileStream = null;
                _memoryStream = null;
                _streamPointer = null;
                _tempFileName = string.Empty;
                GC.Collect();
                //Raise the appropriate event.
                OnStreamClosed(EventArgs.Empty);
            }
        }
        /// <summary>
        /// Copies the current contents of the stream to the new stream instance.
        /// </summary>
        /// <param name="newStream">
        /// The <see cref="Stream"/> implementation instance to which the data is to be copied.
        /// </param>
        public virtual void CopyTo(Stream newStream)
        {
            BinaryWriter newWriter = null;

            newWriter = new BinaryWriter(newStream);
            CopyFileData(newWriter);
            newWriter.Flush();
            newWriter = null;
        }
        /// <summary>
        /// Clones a new instance of the current stream.
        /// </summary>
        /// <returns>
        /// A new <see cref="VirtualMemoryStream"/> instance with the same data.
        /// </returns>
        public VirtualMemoryStream Clone()
        {
            VirtualMemoryStream returnValue = null;             //Return value.

            _streamPointer.Flush();
            returnValue = new VirtualMemoryStream();
            CopyTo(returnValue);

            return returnValue;
        }
        /// <summary>
        /// Flushes any pending writes to the currently open stream.
        /// </summary>
        public override void Flush()
        {
            //Flush the data to the stream.
            try
            {
                if (_writer != null)
                    _writer.Flush();
                if (_streamPointer != null)
                    _streamPointer.Flush();
            }
            catch (IOException ex)
            {
                OnStreamException(new ExceptionEventArgs(ex));
            }

            //Check the size of the data content, and page if necessary.
            if (CheckSize())
                PerformPageOperation();
        }
        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current object instance.
        /// </returns>
        public override int GetHashCode()
        {
            if (_streamPointer != null)
                return _streamPointer.GetHashCode();
            else
                return base.GetHashCode();
        }
        /// <summary>
        /// Obtains a lifetime service object to control the lifetime policy for
        /// the current object instance.
        /// </summary>
        /// <returns>
        /// A lifetime policy object or <b>null</b> if the current stream is closed.
        /// </returns>
        public override object InitializeLifetimeService()
        {
            if (_streamPointer != null)
                return _streamPointer.InitializeLifetimeService();
            else
                return null;
        }
        /// <summary>
        /// Reads data from the current stream into the specified buffer.
        /// </summary>
        /// <param name="buffer">The byte buffer to be filled.</param>
        /// <param name="offset">The index at which to begin filling the byte array.</param>
        /// <param name="count">The number of bytes to be read.</param>
        /// <returns>
        /// The number of bytes copied into the supplied array.
        /// </returns>
        public override int Read(byte[] buffer, int offset, int count)
        {
            int returnValue = 0;                   //Return value.

            if (buffer == null)
            {
                OnStreamException(new ExceptionEventArgs(new ArgumentNullException("buffer")));
                returnValue = 0;
            }
            else
            {
                if (_streamPointer != null)
                {
                    try
                    {
                        returnValue = _streamPointer.Read(buffer, offset, count);
                    }
                    catch (IOException ex)
                    {
                        OnStreamException(new ExceptionEventArgs(ex));
                        returnValue = 0;
                    }
                }
            }
            return returnValue;
        }
        /// <summary>
        /// Reads a byte from the stream at the current position.
        /// </summary>
        /// <returns>
        /// A integer value from the stream containing the byte value if successful; otherwise, returns -1.
        /// </returns>
        public override int ReadByte()
        {
            int returnValue = (int)-1;            //Return value.

            if (_streamPointer != null)
            {
                try
                {
                    returnValue = _streamPointer.ReadByte();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                    returnValue = -1;
                }
            }
            return returnValue;
        }
        /// <summary>
        /// Reads a boolean value from the current position in the stream.
        /// </summary>
        /// <returns>
        /// The current boolean value, if successful; otherwise, returns 0.
        /// </returns>
        public bool ReadBoolean()
        {
            bool returnValue = false;			//Return value.

            if (_reader != null)
            {
                try
                {
                    returnValue = _reader.ReadBoolean();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                    returnValue = false;
                }
            }
            return returnValue;
        }
        /// <summary>
        /// Reads a byte from the current position in the stream.
        /// </summary>
        /// <returns>
        /// The current byte value, if successful; otherwise, returns 0.
        /// </returns>
        public byte ReadAByte()
        {
            byte returnValue = 0;			//Return value.

            if (_reader != null)
            {
                try
                {
                    returnValue = _reader.ReadByte();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                    returnValue = 0;
                }
            }
            return returnValue;
        }
        /// <summary>
        /// Reads an integer from the current position in the stream.
        /// </summary>
        /// <returns>
        /// The current integer value, if successful; otherwise, returns 0.
        /// </returns>
        public short ReadInt16()
        {
            short returnValue = 0;			//Return value.

            if (_reader != null)
            {
                try
                {
                    returnValue = _reader.ReadInt16();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                    returnValue = 0;
                }
            }
            return returnValue;
        }
        /// <summary>
        /// Reads an integer from the current position in the stream.
        /// </summary>
        /// <returns>
        /// The current integer value, if successful; otherwise, returns 0.
        /// </returns>
        public int ReadInt32()
        {
            int returnValue = 0;			//Return value.

            if (_reader != null)
            {
                try
                {
                    returnValue = _reader.ReadInt32();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                    returnValue = 0;
                }
            }
            return returnValue;
        }
        /// <summary>
        /// Reads a long integer from the current position in the stream.
        /// </summary>
        /// <returns>
        /// The current long integer value, if successful; otherwise, returns 0.
        /// </returns>
        public long ReadInt64()
        {
            long returnValue = 0;			//Return value.

            if (_reader != null)
            {
                try
                {
                    returnValue = _reader.ReadInt64();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                    returnValue = 0;
                }
            }
            return returnValue;
        }
        /// <summary>
        /// Reads a floating point number from the current position in the stream.
        /// </summary>
        /// <returns>
        /// The current floating point number value, if successful; otherwise, returns 0.
        /// </returns>
        public float ReadSingle()
        {
            float returnValue = 0;			//Return value.

            if (_reader != null)
            {
                try
                {
                    returnValue = _reader.ReadSingle();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                    returnValue = 0;
                }
            }
            return returnValue;
        }
        /// <summary>
        /// Reads a decimal number from the current position in the stream.
        /// </summary>
        /// <returns>
        /// The current decimal number value, if successful; otherwise, returns 0.
        /// </returns>
        public decimal ReadDecimal()
        {
            decimal returnValue = 0;			//Return value.

            if (_reader != null)
            {
                try
                {
                    returnValue = _reader.ReadDecimal();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                    returnValue = 0;
                }
            }
            return returnValue;
        }
        /// <summary>
        /// Reads a double-floating point number integer from the current position in the stream.
        /// </summary>
        /// <returns>
        /// The current ldouble-floating point number value, if successful; otherwise, returns 0.
        /// </returns>
        public double ReadDouble()
        {
            double returnValue = 0;			//Return value.

            if (_reader != null)
            {
                try
                {
                    returnValue = _reader.ReadDouble();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                    returnValue = 0;
                }
            }
            return returnValue;
        }
        /// <summary>
        /// Reads a string from the current position in the stream.
        /// </summary>
        /// <returns>
        /// The current string, if successful; otherwise, returns <b>null</b>.
        /// </returns>
        public string ReadString()
        {
            string returnValue = null;			//Return value.

            if (_reader != null)
            {
                try
                {
                    returnValue = _reader.ReadString();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                    returnValue = null;
                }
            }
            return returnValue;
        }
        /// <summary>
        /// Saves the contents of the current stream to the specified file.
        /// </summary>
        /// <param name="newFileName">
        /// The name of the file the content is to be written to.
        /// </param>
        /// <returns>
        /// <b>true</b> if the operation completes successfully; otherwise, returns <b>false</b>.
        /// </returns>
        public bool Save(string newFileName)
        {
            FileStream newFile = null;                  //New file pointer.
            BinaryWriter writer = null;                 //Data writer instance.
            bool returnValue = false;                   //Return value.

            //Create the new file.
            newFile = CreateFile(newFileName);
            if (newFile != null)
            {
                writer = new BinaryWriter(newFile);
                if (CopyFileData(writer))
                {
                    //Complete writing successfully.
                    writer.Flush();
                    returnValue = true;
                }
                //Close items.
                writer.Close();
                newFile.Close();
                newFile.Dispose();
            }

            return returnValue;
        }
        /// <summary>
        /// Sets the position within the current stream.
        /// </summary>
        /// <param name="offset">The number of bytes to move, relative ti <i>origin</i>.</param>
        /// <param name="origin">
        /// A <see cref="SeekOrigin"/> enumerated value indicating the point to move the position
        /// relative to.
        /// </param>
        /// <returns></returns>
        public override long Seek(long offset, SeekOrigin origin)
        {
            if (_streamPointer != null)
                return _streamPointer.Seek(offset, origin);
            else
                return 0;
        }
        /// <summary>
        /// Sets the length of the stream.
        /// </summary>
        /// <param name="value">
        /// The maximum size of the stream, in bytes.
        /// </param>
        public override void SetLength(long value)
        {
            //Case where current content is in memoru.
            if (_memoryStream != null)
            {
                if (value <= _memoryBoundary)
                    _memoryStream.SetLength(value);
                else
                {
                    TransferToFileStream();
                    _streamPointer.SetLength(value);
                }
            }
            else
            {
                if (value > _memoryBoundary)
                    _streamPointer.SetLength(value);
                else
                {
                    TransferToMemoryStream((int)value);
                    _memoryStream.SetLength(value);
                }
            }
        }
        /// <summary>
        /// Returns an object representing the current instance.
        /// </summary>
        /// <returns>
        /// A string value.
        /// </returns>
        public override string ToString()
        {
            if (_streamPointer != null)
                return _streamPointer.ToString();
            else
                return CLASS_NAME;
        }
        /// <summary>
        /// Writes the specified data to the current stream.
        /// </summary>
        /// <param name="buffer">A byte array containing the data to be written.</param>
        /// <param name="offset">
        /// The position in <i>buffer</i> at which to begin reading the bytes to be written to
        /// the stream.
        /// </param>
        /// <param name="count">
        /// The number of bytes to be read from the buffer and written to the stream.
        /// </param>
        public override void Write(byte[] buffer, int offset, int count)
        {
            if (_streamPointer != null)
            {
                _streamPointer.Write(buffer, offset, count);
                if (CheckSize())
                    PerformPageOperation();
            }
        }
        /// <summary>
        /// Writes a single byte to the stream.
        /// </summary>
        /// <param name="value">
        /// The byte value to be written.
        /// </param>
        public override void WriteByte(byte value)
        {
            if (_streamPointer != null)
            {
                _streamPointer.WriteByte(value);
                if (CheckSize())
                    PerformPageOperation();
            }
        }
        /// <summary>
        /// Writes a byte of data to the current stream.
        /// </summary>
        /// <param name="data">
        /// A byte of data to be written.
        /// </param>
        public void Write(byte data)
        {
            if (_writer != null)
            {
                try
                {
                    _writer.Write(data);
                    if (CheckSize())
                        PerformPageOperation();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                }
            }
        }
        /// <summary>
        /// Writes a boolean value to the current stream.
        /// </summary>
        /// <param name="data">
        /// A boolean value to be written.
        /// </param>
        public void Write(bool data)
        {
            if (_writer != null)
            {
                try
                {
                    _writer.Write(data);
                    if (CheckSize())
                        PerformPageOperation();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                }
            }
        }
        /// <summary>
        /// Writes a short integer value to the current stream.
        /// </summary>
        /// <param name="data">
        /// A short integer value to be written.
        /// </param>
        public void Write(short data)
        {
            if (_writer != null)
            {
                try
                {
                    _writer.Write(data);
                    if (CheckSize())
                        PerformPageOperation();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                }
            }
        }
        /// <summary>
        /// Writes an integer value to the current stream.
        /// </summary>
        /// <param name="data">
        /// An integer value to be written.
        /// </param>
        public void Write(int data)
        {
            if (_writer != null)
            {
                try
                {
                    _writer.Write(data);
                    if (CheckSize())
                        PerformPageOperation();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                }
            }
        }
        /// <summary>
        /// Writes a long integer value to the current stream.
        /// </summary>
        /// <param name="data">
        /// A long integer value to be written.
        /// </param>
        public void Write(long data)
        {
            if (_writer != null)
            {
                try
                {
                    _writer.Write(data);
                    if (CheckSize())
                        PerformPageOperation();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                }
            }
        }
        /// <summary>
        /// Writes a floating point number value to the current stream.
        /// </summary>
        /// <param name="data">
        /// A floating point number value to be written.
        /// </param>
        public void Write(float data)
        {
            if (_writer != null)
            {
                try
                {
                    _writer.Write(data);
                    if (CheckSize())
                        PerformPageOperation();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                }
            }
        }
        /// <summary>
        /// Writes a decimal number value to the current stream.
        /// </summary>
        /// <param name="data">
        /// A decimal number value to be written.
        /// </param>
        public void Write(decimal data)
        {
            if (_writer != null)
            {
                try
                {
                    _writer.Write(data);
                    if (CheckSize())
                        PerformPageOperation();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                }
            }
        }
        /// <summary>
        /// Writes a double value to the current stream.
        /// </summary>
        /// <param name="data">
        /// A double value to be written.
        /// </param>
        public void Write(double data)
        {
            if (_writer != null)
            {
                try
                {
                    _writer.Write(data);
                    if (CheckSize())
                        PerformPageOperation();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                }
            }
        }
        /// <summary>
        /// Writes a string value to the current stream.
        /// </summary>
        /// <param name="data">
        /// A string value to be written.
        /// </param>
        public void Write(string data)
        {
            if (_writer != null)
            {
                try
                {
                    _writer.Write(data);
                    if (CheckSize())
                        PerformPageOperation();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                }
            }
        }        
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Static/Shared Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Public Static/Shared Methods / Functions
        /// <summary>
        /// Determines the maximum size of memory that can be used before data in the 
        /// <see cref="VirtualMemoryStream"/> class is paged to disk.
        /// </summary>
        /// <returns>
        /// The maximum number of bytes that can be contained in memory.
        /// </returns>
        public static long FindMaximumMemoryBoundary()
        {
            MemoryStream testContainer = null;      //Memory test container.
            BinaryWriter writer = null;             //Data writer.
            byte[] testData = null;                 //Data block(s) to write.
            bool error = false;                     //Error indicator.
            long maxByteSize = 0;                   //Maximum byte size.

            //Clear memory.
            GC.Collect(2);
            
            //Create memory container and test data.
            testContainer = new MemoryStream();
            writer = new BinaryWriter(testContainer);
            testData = new byte[DEF_BLOCK_SIZE];
            testData.Initialize();

            //Write until an error occurs.
            do
            {
                try
                {
                    writer.Write(testData);
                    maxByteSize += DEF_BLOCK_SIZE;
                }
                catch (Exception)
                {
                    error = true;
                }
            } while (!error);

            //Clear memory.
            writer.Close();
            testContainer.Close();
            testContainer.Dispose();
            GC.Collect(2);

            //Set the size to one less block, divide by 2 and subtract 1.
            maxByteSize -= DEF_BLOCK_SIZE;
            maxByteSize /=2;
            maxByteSize -=1;

            return maxByteSize;
        }
        /// <summary>
        /// Finds the maximum size of a <see cref="MemoryStream"/> instance.
        /// </summary>
        /// <returns>
        /// The maximum number of bytes that can be contained in a memory stream object.
        /// </returns>
        public static long FindMaximumAllocationSize()
        {
            long returnValue = 0;

            //Test each size.
            if (TestSize(MEMORYSIZE_256MB))
            {
                returnValue = MEMORYSIZE_256MB;
                if (TestSize(MEMORYSIZE_512MB))
                {
                    returnValue = MEMORYSIZE_512MB;
                    if (TestSize(MEMORYSIZE_1024MB))
                    {
                        returnValue = MEMORYSIZE_1024MB;
                        if (TestSize(MEMORYSIZE_2048MB))
                            returnValue = MEMORYSIZE_2048MB;
                    }
                }
            }
            return returnValue;
        }
        /// <summary>
        /// Tests whether the specified amount of memory may be allocated for a 
        /// <see cref="MemoryStream"/> class.
        /// </summary>
        /// <param name="memorySize">
        /// The number of bytes that are desired to be allocated.
        /// </param>
        /// <returns>
        /// <b>true</b> if the specified memory may be allocatedl; otherwise, <b>false</b>.
        /// </returns>
        public static bool TestSize(int memorySize)
        {
            MemoryStream container = null;
            bool returnValue = false;

            GC.Collect();
            try
            {
                container = new MemoryStream(memorySize);
                returnValue=true;
                container.Close();
                container.Dispose();
            }
            catch
            {
                container=null;
            }
            return returnValue;
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Private Methods / Functions
        /// <summary>
        /// Creates the file with the specified path and name.
        /// </summary>
        /// <param name="newFileToCreate">
        /// The fully-qualified name olf the new file to be created.
        /// </param>
        /// <returns>
        /// A <see cref="FileStream"/> instance if successful; otherwise, returns <b>null</b>.
        /// </returns>
        private FileStream CreateFile(string newFileToCreate)
        {
            FileStream returnValue = null;
            bool exists = false;

            try
            {
                exists = File.Exists(_tempFileName);
            }
            catch (Exception ex)
            {
                OnStreamException(new ExceptionEventArgs(ex));
                exists = false;
            }

            if (exists)
            {
                try
                {
                    File.Delete(_tempFileName);
                }
                catch (Exception ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                }
            }

            try
            {
                returnValue = new FileStream(newFileToCreate, FileMode.CreateNew, FileAccess.Write, FileShare.None);
            }
            catch (IOException ex)
            {
                OnStreamException(new ExceptionEventArgs(ex));
                returnValue = null;
            }

            return returnValue;
        }
        /// <summary>
        /// Copies the contents of the current stream to the open file being 
        /// accessed by the supplied writer.
        /// </summary>
        /// <param name="destWriter">
        /// A <see cref="BinaryWriter"/> instance to an open file.
        /// </param>
        /// <returns>
        /// <b>true</b> if the operation completes successfully; otherwise, returns <b>false</b>.
        /// </returns>
        private bool CopyFileData(BinaryWriter destWriter)
        {
            bool returnValue = false;                       //Return value.
            long position = 0;                              //Current stream position.
            bool error=false;                               //Error marker.

            //Store the current position and re-wind.
            position = _streamPointer.Position;
            _streamPointer.Seek(0, SeekOrigin.Begin);

            do
            {
                try
                {
                    //Read and write in 64K blocks.
                    destWriter.Write(_reader.ReadBytes(COPY_BLOCK_SIZE));
                    destWriter.Flush();
                }
                catch (IOException ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                    error = true;
                }
            } while ((!error) && (_streamPointer.Position < _streamPointer.Length));

            //Flush and return.
            if (!error)
            {
                returnValue = true;
                destWriter.Flush();
            }

            //Return to previous position.
            _streamPointer.Seek(position, SeekOrigin.Begin);

            return returnValue;
        }
        /// <summary>
        /// Initializes the memory stream component for use by the class.
        /// </summary>
        private void InitializeMemoryStream()
        {
            if (_memoryStream == null)
            {
                _memoryStream = new MemoryStream();
                _reader = new BinaryReader(_memoryStream);
                _writer = new BinaryWriter(_memoryStream);
                _tempFileName = string.Empty;
                _fileStream = null;
                _streamPointer = _memoryStream;
            }
        }
        /// <summary>
        /// Initializes the memory stream component for use by the class.
        /// </summary>
        /// <param name="capacity">
        /// The size of the new memory stream instance, in bytes.
        /// </param>
        private void InitializeMemoryStream(int capacity)
        {
            if (_memoryStream == null)
            {
                _memoryStream = new MemoryStream(capacity);
                _reader = new BinaryReader(_memoryStream);
                _writer = new BinaryWriter(_memoryStream);
                _tempFileName = string.Empty;
                _fileStream = null;
            }
        }
        /// <summary>
        /// Initializes the memory stream component for use by the class.
        /// </summary>
        /// <param name="capacity">
        /// The size of the new memory stream instance, in bytes.
        /// </param>
        private void InitializeMemoryStream(long capacity)
        {
            if (_memoryStream == null)
            {
                _memoryStream = new MemoryStream((int)capacity);
                _reader = new BinaryReader(_memoryStream);
                _writer = new BinaryWriter(_memoryStream);
                _tempFileName = string.Empty;
                _fileStream = null;
            }
        }
        /// <summary>
        /// Initializes the memory stream component for use by the class.
        /// </summary>
        /// <param name="content">
        /// The initial content of the memory stream container.
        /// </param>
        private void InitializeMemoryStream(byte[] content)
        {
            if (_memoryStream == null)
            {
                _memoryStream = new MemoryStream(content);
                _reader = new BinaryReader(_memoryStream);
                _writer = new BinaryWriter(_memoryStream);
                _tempFileName = string.Empty;
                _fileStream = null;
            }
        }
        /// <summary>
        /// Initializes a Windows temporary file for use as the underlying stream.
        /// </summary>
        private void InitializeFileStream()
        {
            _fileStream = CreateTempFile();
            if (_fileStream != null)
            {
                _reader = new BinaryReader(_fileStream);
                _writer = new BinaryWriter(_fileStream);
                _streamPointer = _fileStream;
            }
        }
        /// <summary>
        /// Initializes the file stream component for use by the class.
        /// </summary>
        /// <param name="capacity">
        /// The size of the new file stream instance, in bytes.
        /// </param>        
        private void InitializeFileStream(int capacity)
        {
            InitializeFileStream();
            if (_fileStream != null)
                _fileStream.SetLength(capacity);
            _fileStream.Seek(0, SeekOrigin.Begin);

        }
        /// <summary>
        /// Initializes the file stream component for use by the class.
        /// </summary>
        /// <param name="capacity">
        /// The size of the new file stream instance, in bytes.
        /// </param>        
        private void InitializeFileStream(long capacity)
        {
            InitializeFileStream();
            if (_fileStream != null)
                _fileStream.SetLength(capacity);
            _fileStream.Seek(0, SeekOrigin.Begin);
        }
        /// <summary>
        /// Initializes the memory stream component for use by the class.
        /// </summary>
        /// <param name="content">
        /// The initial content of the memory stream container.
        /// </param>
        private void InitializeFileStream(byte[] content)
        {
            InitializeFileStream();
            if (_fileStream != null)
            {
                _writer.Write(content);
                _writer.Flush();
                _fileStream.Seek(0, SeekOrigin.Begin);
            }
        }
        /// <summary>
        /// Creates and opens a new temporary file for use.
        /// </summary>
        /// <returns>
        /// The <see cref="FileStream"/> instance for the current temporary file.
        /// </returns>
        private FileStream CreateTempFile()
        {
            FileStream returnValue = null;              //Return value.

            try
            {
                //Get a new temp file name, and ensure it does not already exist.
                _tempFileName = Path.GetTempFileName();
                SafeDeleteTempFile();

                //Create the new file.
                returnValue = new FileStream(_tempFileName, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None);
                returnValue.Seek(0, SeekOrigin.Begin);

            }
            catch (Exception ex)
            {
                returnValue = null;
                _tempFileName = string.Empty;
                OnStreamException(new ExceptionEventArgs(ex));
            }
            
            return returnValue;
        }
        /// <summary>
        /// Attempts to delete the currently known temporary file.
        /// </summary>
        private void SafeDeleteTempFile()
        {
            bool exists = false;

            try
            {
                exists = File.Exists(_tempFileName);
            }
            catch (Exception ex)
            {
                OnStreamException(new ExceptionEventArgs(ex));
                exists = false;
            }

            if (exists)
            {
                try
                {
                    File.Delete(_tempFileName);
                }
                catch (Exception ex)
                {
                    OnStreamException(new ExceptionEventArgs(ex));
                }
            }
        }
        /// <summary>
        /// Checks the current size of the stream and determines whether a paging operation
        /// is required.
        /// </summary>
        /// <returns>
        /// <b>true</b> if a paging operation is needed; otherwise, <b>false</b>.
        /// </returns>
        private bool CheckSize()
        {
            bool returnValue = false;                   //Return value.

            //If the double-locking mechanism is in use, wait until the lock is released 
            //as a paging operation may be in progress.
            while (_lock != null)
                Thread.Sleep(10);

            //Determine the state change status.
            returnValue = CalculateDelta();

            //Record the current stream size for later comparison.
            _lastSize = _streamPointer.Length;

            return returnValue;
        
        }
        /// <summary>
        /// Calculates the last change in size of the stream.
        /// </summary>
        /// <returns>
        /// <b>true</b> if the last change in the file is significant enough or the file
        /// size is small or large enough to trigger a paging event.
        /// </returns>
        private bool CalculateDelta()
        {
            long difference = 0;                    //Size difference.
            long distance = 0;                      //Distance from memory boundary.
            long currentSize = 0;                   //Current stream size.
            bool returnValue = false;               //Return value.

            if (_streamPointer != null)
            {
                //Comare the current size with the last recorded size.
                currentSize = _streamPointer.Length;
                difference = Math.Abs(_lastSize - currentSize);

                //Compare the distance from the memory boundary.
                distance = Math.Abs(currentSize - _memoryBoundary);

                //Erase the distance parameter if the stream is already in memory and is under memory size, or
                //the stream is on disk and the stream is over memory size.
                if (((_memoryStream != null) && (_streamPointer.Length <= _memoryBoundary)) ||
                    ((_memoryStream == null) && (_streamPointer.Length > _memoryBoundary)))
                {
                    distance = 0;
                }

                //Return a value if either parameter is >= a data block size.
                returnValue = ((difference >= DEF_BLOCK_SIZE) || (distance >= DEF_BLOCK_SIZE));
            }

            return returnValue;
        }
        /// <summary>
        /// Performs the data paging operation.
        /// </summary>
        private void PerformPageOperation()
        {
            lock (this)
            {

                //If the memory usage exceeds the memory boundary, transfer the data to a file.
                if ((_streamPointer.Length >= _memoryBoundary) && (_memoryStream != null))
                {
                    OnMaxMemoryReached(EventArgs.Empty);
                    TransferToFileStream();
                }

                //else if the memory usage is less than the memory boundary, transfer the data from a file to memory.
                else if ((_streamPointer.Length <= _memoryBoundary) && (_memoryStream == null))
                    TransferToMemoryStream((int)_streamPointer.Length);
            }

        }
        /// <summary>
        /// Transfers the content from the memory stream component to a file on disk.
        /// </summary>
        private void TransferToFileStream()
        {
            long oldPosition = 0;                   //Old file pointer position.w

            //Raise the appropriate event.
            OnPageOut(EventArgs.Empty);

            lock (this)
            {
                //Acquire a double-lock.
                _lock = new ReaderWriterLock();
                _lock.AcquireWriterLock(10000);

                //Capture the current cursor position.
                oldPosition = _memoryStream.Position;

                //Copy data to a temp file.
                _memoryStream.Flush();
                _reader = null;
                _writer = null;
                InitializeFileStream(_memoryStream.ToArray());
                if (_fileStream != null)
                {
                    //Move the pointer.
                    _streamPointer = _fileStream;
                    _streamPointer.Position = oldPosition;
                }
                else
                {
                    _fileStream = null;
                    _reader = null;
                    _writer = null;
                }

                //Clear the memory stream.
                _memoryStream.Close();
                _memoryStream.Dispose();
                _memoryStream = null;
                GC.Collect();

                _lock.ReleaseLock();
                _lock = null;
            }
            OnPageComplete(EventArgs.Empty);
        }
        /// <summary>
        /// Transfers the content from the file on disk to memory.
        /// </summary>
        /// <param name="maxBytes">
        /// The number of bytes to be copied to memory.
        /// </param>
        private void TransferToMemoryStream(int maxBytes)
        {
            long oldPosition = 0;                   //Old file pointer position.w

            //Raise the appropriate event.
            OnPageOut(EventArgs.Empty);

            lock (this)
            {
                //Acquire a double-lock.
                _lock = new ReaderWriterLock();
                _lock.AcquireWriterLock(100000);

                //Capture the current cursor position.
                oldPosition = _fileStream.Position;
                _writer.Flush();

                //Copy temp file contents to memory.
                _fileStream.Seek(0, SeekOrigin.Begin);
                InitializeMemoryStream(_reader.ReadBytes(maxBytes));

                //Move the pointer.
                if (_memoryStream != null)
                {
                    _streamPointer = _memoryStream;
                    _streamPointer.Position = oldPosition;
                }

                //Close and delete the temp file.
                _fileStream.Close();
                _fileStream.Dispose();
                _fileStream = null;
                SafeDeleteTempFile();
                _tempFileName = string.Empty;

                _lock.ReleaseLock();
                _lock = null;
            }
            OnPageComplete(EventArgs.Empty);
        }
        #endregion
    }
}
